package com.corpgovernment.organization.service.budget;

import com.corpgovernment.api.costcenter.model.CostCenter;
import com.corpgovernment.api.costcenter.model.GetProjectInfoRequest;
import com.corpgovernment.api.costcenter.model.OrgInfo;
import com.corpgovernment.api.costcenter.service.ICostCenterService;
import com.corpgovernment.api.organization.bo.budget.model.ChangeModel;
import com.corpgovernment.api.organization.bo.budget.model.searchTree.BudgetTypeModel;
import com.corpgovernment.api.organization.bo.budget.model.searchTree.SearchTreeModel;
import com.corpgovernment.api.organization.bo.budget.model.switchTree.CenterModel;
import com.corpgovernment.api.organization.bo.budget.model.switchTree.DepartModel;
import com.corpgovernment.api.organization.bo.budget.model.switchTree.SwitchModel;
import com.corpgovernment.common.base.CommonException;
import com.corpgovernment.common.common.CorpBusinessException;
import com.corpgovernment.common.utils.BaseUtils;
import com.corpgovernment.common.utils.DateUtil;
import com.corpgovernment.costcenter.entity.db.MbOrgProjectDepartment;
import com.corpgovernment.costcenter.entity.db.MbOrgProjectInfo;
import com.corpgovernment.costcenter.mapper.MbOrgProjectCollectMapper;
import com.corpgovernment.costcenter.mapper.MbOrgProjectDepartmentMapper;
import com.corpgovernment.organization.constant.OrganizationResponseCodeEnum;
import com.corpgovernment.organization.dataloader.db.MbOrgProjectClientLoader;
import com.corpgovernment.organization.dataloader.db.MbOrgProjectDepartmentClientLoader;
import com.corpgovernment.organization.constant.OrganizationResponseCodeEnum;
import com.corpgovernment.organization.entity.db.MbOrgEmployeeInfo;
import com.corpgovernment.organization.entity.db.MbOrgInfo;
import com.corpgovernment.organization.entity.db.budget.BudgetGroupEntity;
import com.corpgovernment.organization.entity.db.budget.BudgetInfoEntity;
import com.corpgovernment.organization.enumm.OrgTypeEnum;
import com.corpgovernment.organization.enumm.budget.BudgetChangeEnum;
import com.corpgovernment.organization.enumm.budget.BudgetScopeEnum;
import com.corpgovernment.organization.enumm.budget.BudgetTypeEnum;
import com.corpgovernment.organization.mapper.budget.BudgetGroupMapper;
import com.corpgovernment.organization.mapper.budget.BudgetInfoMapper;
import com.corpgovernment.organization.mapper.budget.BudgetOccupancyMapper;
import com.corpgovernment.organization.service.IOrganizationInfoService;
import com.corpgovernment.organization.service.impl.OrganizationEmployeeServiceImpl;
import com.corpgovernment.organization.util.BudgetDateUtil;
import com.corpgovernment.organization.vo.budget.AddBudgetVo;
import com.corpgovernment.organization.vo.budget.SwitchVo;
import com.ctrip.corp.obt.generic.utils.Assert;
import com.ctrip.corp.obt.generic.utils.CollectionUtils;
import com.ctrip.corp.obt.generic.utils.StringUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class ChangeBudgetService {

    @Autowired
    private IOrganizationInfoService organizationInfoService;
    @Autowired
    private ICostCenterService costCenterService;
    @Autowired
    private BudgetInfoMapper budgetInfoMapper;
    @Autowired
    private BudgetGroupMapper budgetGroupMapper;
    @Autowired
    private BudgetOccupancyMapper budgetOccupancyMapper;
    @Autowired
    private AddBudgetService addBudgetService;
    @Autowired
    private SearchBudgetService searchBudgetService;
    @Autowired
    private MbOrgProjectClientLoader mbOrgProjectClientLoader;
    @Autowired
    private MbOrgProjectDepartmentClientLoader mbOrgProjectDepartmentClientLoader;
    @Autowired
    private OrganizationEmployeeServiceImpl organizationEmployeeService;
    /**
     * 停用/启用
     */
    public void revStop(String budgetId, Integer code) {
        //查询详情
        AddBudgetVo budgetVo  = searchBudgetService.getBudgetVo(budgetId);
        AddBudgetVo newBudgetVo = new AddBudgetVo();
        BeanUtils.copyProperties(budgetVo,newBudgetVo);
        BudgetInfoEntity budgetInfo = budgetInfoMapper.selectByPrimaryKey(budgetId);
        BaseUtils.verifyNotNull(budgetInfo, "没有查询到预算详情");
        BudgetChangeEnum budgetChangeEnum = BudgetChangeEnum.getEnumByCode(code);
        switch (budgetChangeEnum) {
            case INACTIVE:
                budgetInfo.setIsActive(false);
                budgetInfoMapper.updateSelective(budgetInfo);
                newBudgetVo.setStatus(BudgetChangeEnum.INACTIVE.getCode());
                addBudgetService.insertOperatorLog(budgetVo, newBudgetVo);
                return;
            case ACTIVE:
                budgetInfo.setIsActive(true);
                budgetInfoMapper.updateSelective(budgetInfo);
                newBudgetVo.setStatus(BudgetChangeEnum.ACTIVE.getCode());
                addBudgetService.insertOperatorLog(budgetVo, newBudgetVo);
                return;
            default:
                throw new CommonException("操作类型错误");
        }
    }

    /**
     * 删除
     */
    @Transactional
    public ChangeModel deleteBudget(String budgetId) {
        ChangeModel changeModel = new ChangeModel();
        AddBudgetVo budgetVo  = searchBudgetService.getBudgetVo(budgetId);
        List<String> applyNos = budgetOccupancyMapper.selectApplyNoWithOccupyByBudgetId(budgetId);
        if (!CollectionUtils.isEmpty(applyNos)) {
            changeModel.setStatus(1);
            changeModel.setMsg(String.format("该组织下目前包含正在进行预算管控的出差申请单。申请单：%s",
                    String.join("、", Sets.newHashSet(applyNos))));
            return changeModel;
        }
        BudgetInfoEntity budgetInfo = budgetInfoMapper.selectByPrimaryKey(budgetId);
        BaseUtils.verifyNotNull(budgetInfo, "没有查询到预算详情");
        budgetInfo.setIsDeleted(true);
        budgetInfoMapper.updateSelective(budgetInfo);
        List<BudgetGroupEntity> budgetGroups = budgetGroupMapper.selectByBudgetId(budgetInfo.getBudgetId());
        budgetGroups.forEach(g -> {
            g.setIsDeleted(true);
        });
        budgetGroupMapper.updateBatchSelective(budgetGroups);
        budgetVo.setStatus(BudgetChangeEnum.DELETE.getCode());
        addBudgetService.insertOperatorLog(budgetVo, null);
        return changeModel;
    }

    /**
     * 校验切换
     */
    public SwitchModel switchDepCos(SwitchVo switchVo) {
        Assert.notNull(BudgetScopeEnum.getEnumByCode(switchVo.getChangeType()), "预算范围类型错误");
        SwitchModel result = new SwitchModel();
        BudgetInfoEntity budgetInfo = budgetInfoMapper.existsByRangeType(switchVo.getCorpId()
                , BudgetScopeEnum.getEnumByCode(switchVo.getChangeType()).getType()
                , BudgetDateUtil.getFirstDayOfMonth(switchVo.getStartTime())
                , BudgetDateUtil.getLastDayOfMonth(switchVo.getEndTime()));

        if (Objects.isNull(budgetInfo)) {
            result.setIfChange(true);
            Set<String> configs = budgetGroupMapper.selectOrgIdsByCorpId(switchVo.getCorpId(), BudgetDateUtil.getFirstDayOfMonth(switchVo.getStartTime()), BudgetDateUtil.getLastDayOfMonth(switchVo.getEndTime()));
            if (switchVo.getChangeType().equals(BudgetScopeEnum.DEP.getCode())) {
                result.setDepartList(searchDepTree(switchVo.getCorpId(), configs));
            } else if (switchVo.getChangeType().equals(BudgetScopeEnum.COS.getCode())) {
                result.setCenterList(searchCenters(switchVo.getCorpId(), configs));
            } else if (switchVo.getChangeType().equals(BudgetScopeEnum.PRO.getCode())) {
                result.setCenterList(searchProject(switchVo.getCorpId(), configs, BudgetDateUtil.getFirstDayOfMonth(switchVo.getStartTime()), BudgetDateUtil.getLastDayOfMonth(switchVo.getEndTime())));
            }
        } else {
            result.setIfChange(false);
            result.setTitle("您需要重新设置预算时间或范围。");
            result.setContent(String.format("您已设置了%s-%s按%s设置预算。与当前按%s设置的预算时间冲突。",
                    DateUtil.dateToString(budgetInfo.getStartTime(), DateUtil.DF_YM_CN),
                    DateUtil.dateToString(budgetInfo.getEndTime(), DateUtil.DF_YM_CN),
                    BudgetScopeEnum.getEnumByType(budgetInfo.getBudgetType()).getName(),
					BudgetScopeEnum.getEnumByCode(switchVo.getChangeType()).getName()));
		}
		return result;
	}

    /**
     *  查询可配置的项目及 项目的组织范围
     * @param corpId
     * @param configs
     * @return
     */
    private List<CenterModel> searchProject(String corpId, Set<String> configs, Date startTime, Date endTime) {
        List<MbOrgProjectInfo> mbOrgProjectInfos = mbOrgProjectClientLoader.searchProjectByCorpId(Lists.newArrayList(corpId));
        if (CollectionUtils.isEmpty(mbOrgProjectInfos)) {
            return Collections.emptyList();
        }
        mbOrgProjectInfos = mbOrgProjectInfos.stream().filter(project -> {
            if (!Objects.isNull(project.getValidStartDate()) && !Objects.isNull(project.getValidEndDate())) {
                return DateUtil.isEffectiveDate(project.getValidStartDate(), startTime, endTime) || DateUtil.isEffectiveDate(project.getValidEndDate(), startTime, endTime);
            }
            return project.getValid();
        }).collect(Collectors.toList());

        if (CollectionUtils.isEmpty(mbOrgProjectInfos)) {
            return Collections.emptyList();
        }

        List<MbOrgProjectDepartment> mbOrgProjectDepartments = mbOrgProjectDepartmentClientLoader.searchScopeByProId(
                mbOrgProjectInfos.stream().map(MbOrgProjectInfo::getId).collect(Collectors.toList()));

        //显示当前项目配置的范围最新名称
        renewName(mbOrgProjectDepartments);

        Map<Integer, List<MbOrgProjectDepartment>> integerListMap = Optional.ofNullable(mbOrgProjectDepartments).orElse(Lists.newArrayList())
                .stream().collect(Collectors.groupingBy(MbOrgProjectDepartment::getProjectId));



        return mbOrgProjectInfos.stream().map(projectInfo -> {
            CenterModel centerModel = new CenterModel();
            centerModel.setDepartId(projectInfo.getId().toString());
            centerModel.setDepartName(projectInfo.getProjectName());
            centerModel.setIfConfig(configs.contains(centerModel.getDepartId()));
            if (integerListMap.containsKey(projectInfo.getId())) {
                List<MbOrgProjectDepartment> projectDepartments = integerListMap.get(projectInfo.getId());
                if (StringUtils.isNotBlank(projectDepartments.get(0).getUid())) {
                    centerModel.setCorpList(projectDepartments.stream().map(MbOrgProjectDepartment::getUserName).collect(Collectors.toList()));
                } else {
                    centerModel.setCorpList(projectDepartments.stream().map(MbOrgProjectDepartment::getOrgName).collect(Collectors.toList()));
                }

            }
            return centerModel;
        }).collect(Collectors.toList());
    }

    private void renewName(List<MbOrgProjectDepartment> mbOrgProjectDepartments) {
        Map<String, String> orgName = searchOrgName(mbOrgProjectDepartments.stream().filter(projectDepartment -> StringUtils
                .isBlank(projectDepartment.getUid())).map(MbOrgProjectDepartment::getOrgId).collect(Collectors.toList()));

        Map<String, String> userName = searchUserName(mbOrgProjectDepartments.stream().filter(projectDepartment -> StringUtils
                .isNotBlank(projectDepartment.getUid())).map(MbOrgProjectDepartment::getUid).collect(Collectors.toList()));
        for (MbOrgProjectDepartment mbOrgProjectDepartment : mbOrgProjectDepartments) {
            if (StringUtils.isNotBlank(mbOrgProjectDepartment.getUid())) {
                mbOrgProjectDepartment.setUserName(userName.get(mbOrgProjectDepartment.getUid()));
            }
            mbOrgProjectDepartment.setOrgName(orgName.get(mbOrgProjectDepartment.getOrgId()));
        }
    }

    private Map<String, String> searchUserName(List<String> uids) {
        List<MbOrgEmployeeInfo> mbOrgEmployeeInfos = organizationEmployeeService.listByUids(uids);
        return Optional.ofNullable(mbOrgEmployeeInfos)
                .orElse(new ArrayList<>())
                .stream()
                .collect(Collectors.toMap(
                        MbOrgEmployeeInfo::getUid,
                        MbOrgEmployeeInfo::getName,
                        (k1, k2) -> k1
                ));
    }

    private Map<String, String> searchOrgName(List<String> orgIds) {
        List<MbOrgInfo> mbOrgInfos = organizationInfoService.listByOrgIds(orgIds);
        return Optional.ofNullable(mbOrgInfos).orElse(new ArrayList<>()).stream()
                .collect(Collectors.toMap(MbOrgInfo::getOrgId, MbOrgInfo::getName, (k1, k2) -> k1));
    }

    /**
	 * 部门配置项
	 */
	private List<DepartModel> searchDepTree(String corpId, Set<String> configs) {
		List<MbOrgInfo> mbOrgInfoList = organizationInfoService.listAll();
		MbOrgInfo corpInfo = mbOrgInfoList.stream().filter(e -> Objects.equals(e.getOrgId(), corpId)).findFirst().orElse(null);
		if (corpInfo == null) {
			return new ArrayList<>();
		}
		DepartModel corpModel = this.getCorpModel(corpInfo, configs);
		Map<String, List<MbOrgInfo>> parentListMap = mbOrgInfoList.stream().filter(p -> p.getParentId() != null).collect(Collectors.groupingBy(MbOrgInfo::getParentId));
		List<MbOrgInfo> mbOrgInfos = parentListMap.get(corpId);
		if (CollectionUtils.isEmpty(mbOrgInfos)) {
			return new ArrayList<>();
		}
		List<DepartModel> result = Lists.newArrayList();
		for (MbOrgInfo mbOrgInfo : mbOrgInfos) {
			if (!OrgTypeEnum.D.getCode().equals(mbOrgInfo.getOrgType())) {
				continue;
			}
			result.add(findChildDep(mbOrgInfo, parentListMap, configs));
		}
		corpModel.setChildren(result);
		return Lists.newArrayList(corpModel);
	}

	private DepartModel getCorpModel(MbOrgInfo corpInfo, Set<String> configs) {
		DepartModel departModel = new DepartModel();
		departModel.setDepartId(corpInfo.getOrgId());
		departModel.setDepartName(corpInfo.getName());
		departModel.setIfConfig(configs.contains(corpInfo.getOrgId()));
		return departModel;
	}

	private DepartModel findChildDep(MbOrgInfo mbOrgInfo, Map<String, List<MbOrgInfo>> parentListMap, Set<String> configs) {
		DepartModel departModel = new DepartModel();
		departModel.setDepartId(mbOrgInfo.getOrgId());
		departModel.setDepartName(mbOrgInfo.getName());
		departModel.setIfConfig(configs.contains(departModel.getDepartId()));
		departModel.setChildren(Lists.newArrayList());
		List<MbOrgInfo> mbOrgInfos = parentListMap.get(mbOrgInfo.getOrgId());
		if (!CollectionUtils.isEmpty(mbOrgInfos)) {
			for (MbOrgInfo mbOrgInfo1 : mbOrgInfos) {
				departModel.getChildren().add(findChildDep(mbOrgInfo1, parentListMap, configs));
			}
        }
        return departModel;
    }

    /**
     * 成本中心配置项
     */
    private List<CenterModel> searchCenters(String corpId, Set<String> configs) {
        List<CenterModel> centerModels = Lists.newArrayList();
        GetProjectInfoRequest getProjectInfoRequest = new GetProjectInfoRequest();
        getProjectInfoRequest.setCompanyId(corpId);
        List<CostCenter> costCenterList = costCenterService.getCostCentersByCompanyId(getProjectInfoRequest);

        if (CollectionUtils.isEmpty(costCenterList)) {
            log.info(String.format("查询公司[%s]成本中心数据为空", corpId));
            return centerModels;
        }
		costCenterList.forEach(c -> {
            CenterModel centerModel = new CenterModel();
            centerModel.setDepartId(c.getId().toString());
            centerModel.setDepartName(c.getCostCenterName());
            centerModel.setIfConfig(configs.contains(centerModel.getDepartId()));
            if (!CollectionUtils.isEmpty(c.getDepartments())) {
                centerModel.setCorpList(c.getDepartments().stream().map(OrgInfo::getName).collect(Collectors.toList()));
            }
            centerModels.add(centerModel);
        });
        return centerModels;
    }

    /**
     * 预算类型配置项
     */
    public SearchTreeModel searchBudgetTypes() {
        return new SearchTreeModel(
                Arrays.stream(BudgetTypeEnum.values()).map(t -> new BudgetTypeModel(t.getType(), t.getName())).collect(Collectors.toList())
        );
    }
}